package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {

    private ServerSocket serverSocket = null;

    //指定端口号
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("启动服务器！！！");

        ExecutorService service = Executors.newCachedThreadPool();
        while(true){
            // accept 相当于针对内核中已经建立好的连接进行 “确认” 的动作
            Socket clientSocket = serverSocket.accept();

            //使用多线程解决同一个服务器服务于多个客户端
            //创建线程，每个线程服务于一个客户端
            Thread t = new Thread(() ->{
                try {
                    processConnection(clientSocket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            t.start();

            //使用线程池
            service.submit(() -> {
                try {
                    processConnection(clientSocket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });

        }
    }

    //针对一个连接，提供处理逻辑
    private void processConnection(Socket clientSocket) throws IOException {
        //先打印一下客户端的信息
        System.out.printf("[%s:%d] 客户端上线！！！\n",clientSocket.getInetAddress(),clientSocket.getPort());

        //获取到 socket 中持有的流对象
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){

            //使用 Scanner 包装一下 inputStream ，就可以更方便的读取这里的请求数据了
            Scanner scan = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while(true){
                // 1 . 读取请求并解析
                if(!scan.hasNext()){
                    //如果 scan 中无法读取数据，则说明客户端关闭了连接
                    //导致服务器这边读取到 “末尾”
                    break ;
                }
                String request = scan.next();

                // 2 . 根据请求计算响应
                String response = process(request);

                // 3 . 将响应写回客户端
                //此处可以按照字节数组直接来写，也可以有另一种写法
//                outputStream.write(response.getBytes());
                printWriter.println(response);
                printWriter.flush();
                //此处用 prrintln 的原因是：
                //读取数据的时候，隐藏了条件，请求应该是以 ”空白符“ 结尾的
                //包括但不限于：空格、回车、制表符、垂直制表符、翻页付
                //所以此处就约定，使用 “\n” 来作为请求和响应的结尾

                // 4 . 打印日志
                System.out.printf("[%s:%d] rep = %s ; resp = %s\n",clientSocket.getInetAddress(),clientSocket.getPort(),
                        request,response);

            }


        }catch(IOException e){
            e.printStackTrace();
        }finally {
            System.out.printf("[%s:%d] 客户端下线！！！\n",clientSocket.getInetAddress(),clientSocket.getPort());
            clientSocket.close();
        }
    }

    //请求什么，则返回什么响应
    private String process(String request) {
        return request;
    }


    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }

}
